use core::{arch::global_asm, mem::size_of};

use sizes::{SZ_4G, SZ_4K};

use self::pt_regs::PtRegs;
use crate::{kalloc::Page, main};

pub mod pt_regs;

global_asm!(include_str!("asm/define.S"));
global_asm!(include_str!("asm/macro.S"));
global_asm!(include_str!("asm/head.S"));
global_asm!(include_str!("asm/cache.S"));
global_asm!(include_str!("asm/entry.S"));
global_asm!(include_str!("asm/clear_page.S"));

/// # Safety
/// 由启动代码调用
#[doc(hidden)]
#[no_mangle]
pub unsafe extern "C" fn start_kernel() {
    main()
}

/// # Safety
/// 由启动代码调用
#[doc(hidden)]
#[no_mangle]
pub unsafe extern "C" fn handle_arch_irq() {
    panic!("handle_arch_irq");
}

/// # Safety
/// 由启动代码调用
#[doc(hidden)]
#[no_mangle]
pub unsafe extern "C" fn bad_mode(_regs: &mut PtRegs, _reason: i32, _esr: u32) {
    panic!("bad_mode");
}

/// # Safety
/// 由启动代码调用
#[doc(hidden)]
#[no_mangle]
pub unsafe extern "C" fn do_mem_abort(_addr: usize, _esr: u32, _regs: &mut PtRegs) {
    panic!("do_mem_abort");
}

/// # Safety
/// 由启动代码调用
#[doc(hidden)]
#[no_mangle]
pub unsafe extern "C" fn do_sp_pc_abort(_addr: usize, _esr: u32, _regs: &mut PtRegs) {
    panic!("do_sp_pc_abort");
}

#[doc(hidden)]
#[no_mangle]
pub unsafe extern "C" fn do_undefinstr(_regs: &mut PtRegs) {
    panic!("do_undefinstr");
}

#[doc(hidden)]
#[no_mangle]
pub unsafe extern "C" fn do_debug_exception(_addr: usize, _esr: u32, _regs: &mut PtRegs) {
    panic!("do_debug_exception");
}

#[doc(hidden)]
#[no_mangle]
pub unsafe extern "C" fn preempt_schedule_irq() {
    panic!("preempt_schedule_irq");
}

#[doc(hidden)]
#[no_mangle]
pub unsafe extern "C" fn do_el0_ia_bp_hardening(_addr: usize, _esr: u32, _regs: &mut PtRegs) {
    panic!("do_el0_ia_bp_hardening");
}

#[doc(hidden)]
#[no_mangle]
pub unsafe extern "C" fn do_fpsimd_acc(_esr: u32, _regs: &mut PtRegs) {
    panic!("do_fpsimd_acc");
}

#[doc(hidden)]
#[no_mangle]
pub unsafe extern "C" fn do_sve_acc(_esr: u32, _regs: &mut PtRegs) {
    panic!("do_sve_acc");
}

#[doc(hidden)]
#[no_mangle]
pub unsafe extern "C" fn do_fpsimd_exc(_esr: u32, _regs: &mut PtRegs) {
    panic!("do_fpsimd_exc");
}

#[doc(hidden)]
#[no_mangle]
pub unsafe extern "C" fn do_sysinstr(_esr: u32, _regs: &mut PtRegs) {
    panic!("do_sysinstr");
}

#[doc(hidden)]
#[no_mangle]
pub unsafe extern "C" fn do_notify_resume(_regs: &mut PtRegs, _thread_flags: u32) {
    panic!("do_notify_resume");
}

#[doc(hidden)]
#[no_mangle]
pub unsafe extern "C" fn el0_svc_handler(_regs: &mut PtRegs) {
    panic!("el0_svc_handler");
}

#[doc(hidden)]
#[no_mangle]
pub unsafe extern "C" fn do_serror(_regs: &mut PtRegs, _esr: u32) {
    panic!("do_serror");
}

#[doc(hidden)]
#[no_mangle]
pub unsafe extern "C" fn bad_el0_sync(_regs: &mut PtRegs, _reason: i32, _esr: u32) {
    panic!("bad_el0_sync");
}

// 由链接器或者汇编导出的符号和函数在此申明
extern "C" {
    static _text: usize;
    static _end: usize;
    static _stack_end: usize;

    /// 快速清零一页
    pub fn clear_page(to: *mut core::ffi::c_void);
}

/// 内核起始虚拟地址
#[inline]
pub fn start_text() -> usize {
    unsafe { (&_text as *const usize) as usize }
}

/// 内核结束虚拟地址
#[inline]
pub fn end_bss() -> usize {
    unsafe { (&_end as *const usize) as usize }
}

#[inline]
pub fn init_stack_end() -> usize {
    unsafe { (&_stack_end as *const usize) as usize }
}

// 内核代码的起始偏移地址, 可实现物理地址和内核地址的互相转换
const KIMAGE_OFFSET: usize = 0xffff000000000000;

/// 物理地址转换为内核地址
pub const fn phys_to_kimg(paddr: usize) -> usize {
    paddr + KIMAGE_OFFSET
}

/// 内核地址转换为物理地址
pub const fn kimg_to_phys(kaddr: usize) -> usize {
    kaddr - KIMAGE_OFFSET
}

// 内核 io 起始偏移地址, 可实现物理地址和 io 地址的互相转换
// 注意:
// io 地址是通过连续映射 0-16G 的物理地址实现的, 因此 io 地址最大不能超过 16G 范围
const KIO_OFFSET: usize = 0xffff001000000000;

/// 物理地址转换为 io 地址
pub const fn phys_to_io(paddr: usize) -> usize {
    paddr + KIO_OFFSET
}

/// io 地址转换为物理地址
pub const fn io_to_phys(kio: usize) -> usize {
    kio - KIO_OFFSET
}

// 线性内存起始偏移地址, 可实现物理地址和线性地址的互相转换
// 注意:
// 线性地址是通过连续映射 0-16G 的物理地址实现的, 因此线性地址最大不能超过 16G 范围
const KMEM_OFFSET: usize = 0xffff002000000000;

/// 物理地址转换为线性地址
pub const fn phys_to_virt(paddr: usize) -> usize {
    paddr + KMEM_OFFSET
}

/// 线性地址转换为物理地址
pub const fn virt_to_phys(vaddr: usize) -> usize {
    vaddr - KMEM_OFFSET
}

/// 线性地址转换为页帧
pub const fn virt_to_pfn(vaddr: usize) -> usize {
    phys_to_pfn(virt_to_phys(vaddr))
}

/// 线性地址转换为页结构
#[inline(always)]
pub const fn virt_to_page(vaddr: usize) -> &'static Page {
    phys_to_page(virt_to_phys(vaddr))
}

/// 线性地址转换为页结构(可变的)
#[inline(always)]
pub const fn virt_to_page_mut(vaddr: usize) -> &'static mut Page {
    phys_to_page_mut(virt_to_phys(vaddr))
}

/// 页大小
pub const PAGE_SIZE: usize = SZ_4K;
/// 页偏移
pub const PAGE_SHIFT: usize = 12;
/// 页掩码
pub const PAGE_MASK: usize = !(PAGE_SIZE - 1);

/// 物理地址起始位置
///
/// 注意:
///
/// 这里实现只能支持连续的内存地址块, 中间不能存在内从空洞
pub const MEMSTART: usize = 0x40000000;
/// 物理地址大小
pub const MEMSIZE: usize = SZ_4G;
/// 物理地址结束
pub const MEMEND: usize = MEMSTART + MEMSIZE;

/// 物理页转换为页帧
pub const fn phys_to_pfn(phys: usize) -> usize {
    (phys - MEMSTART) >> PAGE_SHIFT
}

/// 物理页转换为页帧并向上 PAGE_SIZE 对齐
pub const fn phys_to_pfn_up(mut phys: usize) -> usize {
    phys += PAGE_SIZE - 1;
    (phys - MEMSTART) >> PAGE_SHIFT
}

/// 页帧转换为物理地址
pub const fn pfn_to_phys(pfn: usize) -> usize {
    (pfn << PAGE_SHIFT) + MEMSTART
}

// 页结构地址偏移, 可用于通过其他地址快速找到对应的页结构
const KPAGE_OFFSET: usize = 0xffff003000000000;
// 页结构大小
const KPAGE_SIZE: usize = size_of::<Page>();
// 页结构总数量
const KPAGE_COUNT: usize = MEMSIZE / PAGE_SIZE;
// 页结构占用总内存量
const KPAGE_MEM: usize = KPAGE_SIZE * KPAGE_COUNT;
// 实际的页结构起始地址
// 通过定义为物理地址高位位置到内存结束区域为页结构存储空间
const KPAGE_START: usize = KPAGE_OFFSET + kpage_start_phys();

/// 页结构起始地址
pub const fn kpage_start() -> usize {
    KPAGE_START
}

/// 页结构结束地址
pub const fn kpage_end() -> usize {
    KPAGE_START + KPAGE_MEM
}

pub const fn kpage_start_phys() -> usize {
    MEMEND - KPAGE_MEM
}

/// 物理地址转换为一个页结构
///
/// 注意:
///
/// 这里的设计需要保证内存是连续的, 没有空洞内存,
/// 因为 KPAGE 是连续内存映射的.
pub const fn phys_to_page(paddr: usize) -> &'static Page {
    let index = phys_to_pfn(paddr);
    unsafe { &*(KPAGE_START as *const Page).add(index) }
}

/// 物理地址转换为一个页结构(可修改页结构)
pub const fn phys_to_page_mut(paddr: usize) -> &'static mut Page {
    let index = (paddr - MEMSTART) >> PAGE_SHIFT;
    unsafe { &mut *(KPAGE_START as *mut Page).add(index) }
}

/// 页结构转换为物理地址
#[inline(always)]
pub fn page_to_phys(page: &Page) -> usize {
    let p = page as *const Page as usize;
    (((p - KPAGE_START) / KPAGE_SIZE) << PAGE_SHIFT) + MEMSTART
}

/// 页结构转换为页帧
#[inline(always)]
pub fn page_to_pfn(page: &Page) -> usize {
    phys_to_pfn(page_to_phys(page))
}

/// 页结构转换为线性地址
#[inline(always)]
pub fn page_to_virt(page: &Page) -> usize {
    phys_to_virt(page_to_phys(page))
}

#[derive(Clone, Copy)]
struct MemBlockRange {
    start: usize,
    size: usize,
}

// 定义有 16 块, 目前足够
const MEM_RANG_MAX: usize = 16;

struct MemBlock {
    index: usize,
    range: [MemBlockRange; MEM_RANG_MAX],
}

// 定义一个全局的可用内存范围, 最多可以设置 MEM_RANG_MAX 块内存区域
static mut MEMBLOCK_RANGE: MemBlock =
    MemBlock { index: 0, range: [MemBlockRange { start: 0, size: 0 }; MEM_RANG_MAX] };

/// 添加一块内存域
///
/// 注意: 添加内存域不能与已添加的内存域交叉并且不能添加超过 MEM_RANG_MAX, 否则将会断言失败
///
/// # Safety:
/// 只能由主核启动时添加, 否则存在并发问题
pub fn memblock_add(start: usize, size: usize) {
    let end = start + size;
    unsafe {
        let index = MEMBLOCK_RANGE.index;
        assert!(index < MEM_RANG_MAX);
        for i in 0..index {
            let mem_end = MEMBLOCK_RANGE.range[i].start + MEMBLOCK_RANGE.range[i].size;
            assert!(end <= MEMBLOCK_RANGE.range[i].start || mem_end <= start);
        }
        MEMBLOCK_RANGE.range[index].start = start;
        MEMBLOCK_RANGE.range[index].size = size;
        MEMBLOCK_RANGE.index += 1;
    }
}

/// 判断页帧是否是一个可用的页帧
///
/// 判断标准则是根据页帧是否在添加的内存域为标准
pub fn pfn_valid(pfn: usize) -> bool {
    unsafe {
        let index = MEMBLOCK_RANGE.index;
        for i in 0..index {
            let start = MEMBLOCK_RANGE.range[i].start;
            let end = start + MEMBLOCK_RANGE.range[i].size;
            let start_pfn = phys_to_pfn(start);
            let end_pfn = phys_to_pfn(end);
            if pfn >= start_pfn && pfn < end_pfn {
                return true;
            }
        }
    }
    false
}

/// 遍历内存域
pub fn memblock_foreach<F>(f: F)
where
    F: Fn(usize, usize),
{
    unsafe {
        let index = MEMBLOCK_RANGE.index;
        for i in 0..index {
            let start = MEMBLOCK_RANGE.range[i].start;
            let size = MEMBLOCK_RANGE.range[i].size;
            f(start, size)
        }
    }
}

/// 全局可用内存域设置
///
/// f: start, size, is_free
pub fn mem_setup<F>(f: F)
where
    F: Fn(usize, usize, bool),
{
    let end = pfn_to_phys(phys_to_pfn(kimg_to_phys(start_text())));
    memblock_add(MEMSTART, end - MEMSTART);
    f(MEMSTART, end - MEMSTART, true);

    let start = pfn_to_phys(phys_to_pfn_up(kimg_to_phys(end_bss())));
    f(end, start - end, false);
    let end = kpage_start_phys();
    memblock_add(start, end - start);
    f(start, end - start, true);
    f(end, MEMEND - end, false);
}

/// 系统有 8 个 cpu
pub const NR_CPUS: usize = 8;

pub const VA_BITS: usize = 48;

pub const USER_DS: usize = (1 << VA_BITS) - 1;
pub const KERNEL_DS: usize = usize::MAX;

#[no_mangle]
pub static IRQ_STACK_PTR: [usize; NR_CPUS] = [0; NR_CPUS];
